//默认初始化，如果

//初始化(等级，节点数据,本地保存)

//访问服务端存在
//访问节点存在
//创建节点
//异步写
//同步写
//4个等级写
package holmes

import (
	//"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"runtime"

	//"sync"
	"flag"
	"sync/atomic"
	"time"
)

//配置
var level *string
var distributeMode *bool
var http_log_server *string
var nameInLogServer *string

//初始化，将部分flag参数声明，移动到各自包中的init函数中,注意flag.parse必须在main函数中调用
func init() {
	level = flag.String("level", "info", "设置日志输出等级 debug/info/warn/error")
	distributeMode = flag.Bool("distributeLog", true, "分布式日志模式开启")
	http_log_server = flag.String("logHttp", "127.0.0.1:8082", "设置日志服务器地址")
	nameInLogServer = flag.String("logNodeName", "默认节点", "日志服务器记录时的节点名称")
}

//根据flag自动化启动
func AutoStart() *Logger {
	defer func() {
		Infoln("日志输出等级:", *level)
		Infoln("分布式日志模式:", *distributeMode)
		if *distributeMode {
			Infoln("日志服务器地址", *http_log_server)
			Infoln("日志服务器记录时的节点名称", *nameInLogServer)
		}
	}()

	//还未调用解析,说明没有其他包使用flag
	if !flag.Parsed() {
		flag.Parse()
	}

	//分布式日志
	if *distributeMode {
		return Start(SetLevel(*level), SetDistributeMode(*nameInLogServer, *http_log_server, "", "INFO"))
	} else {
		return Start(SetLevel(*level))
	}
}

// LogLevel is the log level type.
type LogLevel int

const (
	// DEBUG represents debug log level.
	DEBUG LogLevel = iota
	// INFO represents info log level.
	INFO
	// WARN represents warn log level.
	WARN
	// ERROR represents error log level.
	ERROR
	//
	EVENT
	// FATAL represents fatal log level.
	FATAL
)

type IoWriterMode int

const (
	//输出模式
	//本地模式
	IoWriterStdMode = iota
	//文件模式
	IoWriterFileMode
	//网络模式
	IoWriterNetMode
	//Pipe模式
	IoWriterPipeMode
	//混合模式1:std+file
	IoWriterStdFileMode
)
const defaultIoWriterMode = IoWriterStdMode

var (
	started        int32
	loggerInstance Logger
	tagName        = map[LogLevel]string{
		DEBUG: "DEBUG",
		INFO:  "INFO",
		WARN:  "WARN",
		ERROR: "ERROR",
		EVENT: "EVENT",
		FATAL: "FATAL",
	}
)

func LevelFromString(input string) LogLevel {
	for key, value := range tagName {
		if input == value {
			return key
		}
	}
	return INFO
}

//默认std模式
// Start returns a decorated innerLogger.
func Start(decorators ...func(Logger) Logger) *Logger {
	if atomic.CompareAndSwapInt32(&started, 0, 1) {
		loggerInstance = Logger{}
		for _, decorator := range decorators {
			loggerInstance = decorator(loggerInstance)
		}
		var logger *log.Logger
		if loggerInstance.writesManagement != nil {
			logger = log.New(loggerInstance, "", log.Lmicroseconds|log.LstdFlags)
		} else {
			logger = log.New(os.Stderr, "", log.Lmicroseconds|log.LstdFlags)
		}
		loggerInstance.logger = logger
		return &loggerInstance
	}
	panic("Start() already called")
}

// Stop stops the logger.
func (l Logger) Stop() {
	if atomic.CompareAndSwapInt32(&l.stopped, 0, 1) {
		if l.printStack {
			traceInfo := make([]byte, 1<<16)
			n := runtime.Stack(traceInfo, true)
			l.logger.Printf("%s", traceInfo[:n])
			if l.isStdout {
				log.Printf("%s", traceInfo[:n])
			}
		}
		if l.writesManagement != nil {
			l.writesManagement.Close()
		}
		l.writesManagement = nil
		l.logger = nil
		atomic.StoreInt32(&started, 0)
	}
}

type writesManagement interface {
	//写入
	io.WriteCloser
	//配置
	config(map[string]interface{}) error
	mode() IoWriterMode
}

// Logger is the logger type.
type Logger struct {
	logger     *log.Logger
	level      LogLevel
	stopped    int32
	unit       time.Duration
	isStdout   bool
	printStack bool
	//写入者
	writesManagement
	//网络连接或pipe模式
	internet *internetClient
}

func (l Logger) closeInternet() {
	if *distributeMode && l.internet != nil {
		l.internet.closePoll()
	}
}
func (l Logger) isInternetOver() bool {
	if *distributeMode && l.internet != nil {
		return l.internet.over()
	}
	return true
}

func (l Logger) doPrintf(level LogLevel, format string, v ...interface{}) {
	if l.logger == nil {
		return
	}
	if level >= l.level {
		funcName, fileName, lineNum := getRuntimeInfo()
		header := fmt.Sprintf("%5s [%s] (%s:%d) - ", tagName[level], path.Base(funcName), path.Base(fileName), lineNum)
		value := fmt.Sprintf(format, v...)
		l.logger.Print(header + value)
		if l.isStdout {
			log.Print(header + value)
		}
		if l.internet != nil {
			l.internet.send2server(time.Now().UnixNano(), level, fmt.Sprintf("[%s] (%s:%d) - ", path.Base(funcName), path.Base(fileName), lineNum)+value)
		}
		if level == FATAL {
			os.Exit(1)
		}
	}
}

func (l Logger) doPrintln(level LogLevel, v ...interface{}) {
	if l.logger == nil {
		return
	}
	if level >= l.level {
		funcName, fileName, lineNum := getRuntimeInfo()
		prefix := fmt.Sprintf("%5s [%s] (%s:%d) - ", tagName[level], path.Base(funcName), path.Base(fileName), lineNum)
		true_msg := fmt.Sprintln(v...)
		value := fmt.Sprintf("%s%s", prefix, true_msg)
		l.logger.Print(value)
		if l.isStdout {
			log.Print(value)
		}
		if l.internet != nil {
			l.internet.send2server(time.Now().UnixNano(), level, fmt.Sprintf("[%s] (%s:%d) - ", path.Base(funcName), path.Base(fileName), lineNum)+true_msg)
		}
	}
}

func getRuntimeInfo() (string, string, int) {
	pc, fn, ln, ok := runtime.Caller(3) // 3 steps up the stack frame
	if !ok {
		fn = "jQuery21104404735479917472_1492734101560?"
		ln = 0
	}
	var function string
	caller := runtime.FuncForPC(pc)
	if caller != nil {
		function = caller.Name()
	}
	return function, fn, ln
}

// DebugLevel sets log level to debug.
func DebugLevel(l Logger) Logger {
	l.level = DEBUG
	return l
}

// InfoLevel sets log level to info.
func InfoLevel(l Logger) Logger {
	l.level = INFO
	return l
}

// WarnLevel sets log level to warn.
func WarnLevel(l Logger) Logger {
	l.level = WARN
	return l
}

// ErrorLevel sets log level to error.
func ErrorLevel(l Logger) Logger {
	l.level = ERROR
	return l
}

// FatalLevel sets log level to fatal.
func FatalLevel(l Logger) Logger {
	l.level = FATAL
	return l
}

func SetLevel(level string) func(Logger) Logger {
	switch level {
	case "debug":
		return DebugLevel
	case "info":
		return InfoLevel
	case "warn":
		return WarnLevel
	case "error":
		return ErrorLevel
	default:
		return InfoLevel
	}

}
